//=============================================================================

#include <fstream>
#include "../inc/aCharacter.h"

//=============================================================================

using namespace std;

//=============================================================================

const long charMagico = (~-111);
const long objMagico = (~777);

//=============================================================================

aCharacter::aCharacter(unsigned long setId) : aObject(setId)
{
  lifeType = LIFE_NONE;
}

//=============================================================================

void aCharacter::aoObjectProcess()
{
  aItem<aAction>* tmpAction = actions.alGetFirstItem();

  while(tmpAction)
  {
    switch(tmpAction->data.aaGetType())
    {
      case ACT_CHOOSED:
      {
        break;
      }
      default:
      {
        break;
      }
    }
    tmpAction = tmpAction->next;
  }
}

//=============================================================================

acrRet aCharacter::acAddAttack(aString cap, aString anm, float range, DWORD timeout)
{
  if (cap.asGetLength() <= 0)
    return CHAR_CANT_ADD_ATTACK_BAD_NAME;

  if (anm.asGetLength() <= 0)
    return CHAR_CANT_ADD_ATTACK_BAD_ANIMATION_NAME;

  if (!aoHasAnimation(anm))
    return CHAR_CANT_ADD_ATTACK_NO_SUCH_ANIMATION;

  if (range <= 0.0f)
    return CHAR_CANT_ADD_ATTACK_BAD_RANGE;

  if (timeout < 500)
    return CHAR_CANT_ADD_ATTACK_TO_SMALL_TIMEOUT;

  aItem<aAttack>* tmpAttack = attacks.alAddItem();

  tmpAttack->data.name = cap;
  tmpAttack->data.animName = anm;
  tmpAttack->data.range = range;
  tmpAttack->data.timeout = timeout;

  return CHAR_DONE;
}

//=============================================================================

acrRet aCharacter::acAppendAttackData(aString cap, aString anm, float range, DWORD timeout)
{
  if (cap.asGetLength() <= 0)
    return CHAR_CANT_APPEND_ATTACK_BAD_NAME;

  if (anm.asGetLength() <= 0)
    return CHAR_CANT_APPEND_ATTACK_BAD_ANIMATION_NAME;

  if (!aoHasAnimation(anm))
    return CHAR_CANT_APPEND_ATTACK_NO_SUCH_ANIMATION;

  if (range <= 0.0f)
    return CHAR_CANT_APPEND_ATTACK_BAD_RANGE;

  if (timeout < 500)
    return CHAR_CANT_APPEND_ATTACK_TO_SMALL_TIMEOUT;

  aItem<aAttack>* tmpAttack = attacks.alGetFirstItem();

  while(tmpAttack)
  {
    if (tmpAttack->data.name == cap)
    {
      tmpAttack->data.animName = anm;
      tmpAttack->data.range = range;
      tmpAttack->data.timeout = timeout;

      break;
    }
    tmpAttack = tmpAttack->next;
  }

  if (!tmpAttack)
    return CHAR_CANT_APPEND_NO_SUCH_ATTACK;

  return CHAR_DONE;
}

//=============================================================================

size_t aCharacter::acGetAttacksDataVolume() const
{
  size_t res = 0;
  int count = attacks.alGetCount();

  if (count <= 0)
    return 0;

  res += sizeof(int);//count of attacks;
  res += sizeof(float)*count;//range
  res += sizeof(DWORD)*count;//timeout
  res += sizeof(size_t)*2*count;//name & animName lengths
  
  aItem<aAttack>* tmpAttack = attacks.alGetFirstItem();

  while(tmpAttack)
  {
    res += tmpAttack->data.name.asGetLength()*tmpAttack->data.name.asGetCharSize();
    res += tmpAttack->data.animName.asGetLength()*tmpAttack->data.animName.asGetCharSize();
    tmpAttack = tmpAttack->next;
  }

  return res;
}

//=============================================================================

acrRet aCharacter::acCollectAttacksData(void* attack, size_t wholeSize) const
{
  if (!attack)
    return CHAR_COL_BAD_DATA_POINTER;

  if (wholeSize <= 0)
    return CHAR_COL_BAD_VOLUME_SIZE;

  aString tmpString;
  size_t shift = 0;
  size_t intSize = sizeof(int);
  size_t floatSize = sizeof(float);
  size_t dwordSize = sizeof(DWORD);
  size_t sizeSize = sizeof(size_t);
  size_t charSize = tmpString.asGetCharSize();
  size_t tmpLength;
  int count = attacks.alGetCount();
  char* tmpPointer = (char*)attack;
  aItem<aAttack>* tmpAttack = attacks.alGetFirstItem();

  memcpy(tmpPointer, &count, intSize);
  shift += intSize;

  while(tmpAttack)
  {
    memcpy(&tmpPointer[shift], &tmpAttack->data.range, floatSize);
    shift += floatSize;

    memcpy(&tmpPointer[shift], &tmpAttack->data.timeout, dwordSize);
    shift += dwordSize;

    tmpLength = tmpAttack->data.name.asGetLength();
    memcpy(&tmpPointer[shift], &tmpLength, sizeSize);
    shift += sizeSize;

    memcpy(&tmpPointer[shift], tmpAttack->data.name.asGetString(), tmpLength*charSize);
    shift += tmpLength*charSize;

    tmpLength = tmpAttack->data.animName.asGetLength();
    memcpy(&tmpPointer[shift], &tmpLength, sizeSize);
    shift += sizeSize;

    memcpy(&tmpPointer[shift], tmpAttack->data.animName.asGetString(), tmpLength*charSize);
    shift += tmpLength*charSize;

    tmpAttack = tmpAttack->next;
  }

  return CHAR_DONE;
}

//=============================================================================

aotRet aCharacter::aoLoadObject(const aString& path)
{
  if (path.asGetLength() <= 0)
    return OBJ_BAD_PATH;

  fstream file;

  file.open(path.asGetString(), ios_base::in | ios_base::binary);

  if (!file.is_open())
    return OBJ_CANT_OPEN_OBJECT_FILE;

  //read the header;
  long magicNumber = 0;
  size_t nameLength;

  file.read((char*)&magicNumber, sizeof(magicNumber));

  if ((magicNumber != charMagico) && (magicNumber != objMagico))
    return OBJ_BAD_MAGIC_IN_OBJECT_FILE;

  aoClearObjectData();

  file.read((char*)&nameLength, sizeof(size_t));

  wchar_t* namePtr = new wchar_t[nameLength + 1];
  const int headerCounter = 4;
  const int headerCounter2 = 5;
  bool isChar = (magicNumber == charMagico ? 1:0);
  int counts[headerCounter];// 0 - vertexes
                            // 1 - textures
                            // 2 - joints
                            // 3 - animations

  size_t volumes[headerCounter2]; // 0 - surface volume
                                  // 1 - skin volume
                                  // 2 - skeleton volume
                                  // 3 - anim volume
                                  // 4 - attacks volume

  file.read((char*)namePtr, nameLength*name.asGetCharSize());
  namePtr[nameLength] = 0;
  name = namePtr;

  file.read((char*)&health, sizeof(int));
  file.read((char*)&mana, sizeof(int));
  file.read((char*)&stamina, sizeof(int));

  file.read((char*)counts, sizeof(int)*headerCounter);
  file.read((char*)volumes, sizeof(size_t)*(headerCounter2 - (isChar ? 0:1)));
  if (isChar)
    file.read((char*)&lifeType, sizeof(acrLife));

  char* vertexesData = new char[volumes[0]];

  file.read((char*)vertexesData, volumes[0]);

  switch(mainModel.asAbsorbSurfaceData(vertexesData, counts[0]))
  {
    case SURF_ABS_BAD_DATA_POINTER:
    {
      delete [] namePtr;
      delete [] vertexesData;

      file.close();

      return OBJ_SURFACE_ABS_BAD_DATA_POINTER;
    }
    case SURF_ABS_BAD_VERTEXES_COUNT:
    {
      delete [] namePtr;
      delete [] vertexesData;

      file.close();

      return OBJ_SURFACE_ABS_BAD_VERTEXES_COUNT;
    }
    default:
    {
      break;
    }
  }

  char* texData = new char[volumes[1]];

  file.read((char*)texData, volumes[1]);

  switch(mainModel.asAbsorbSkinData(texData, counts[1]))
  {
    case ABS_BAD_SKIN_DATA_POINTER:
    {
      delete [] namePtr;
      delete [] vertexesData;
      delete [] texData;

      file.close();

      return OBJ_SKIN_ABS_BAD_DATA_POINTER;
    }
    case ABS_BAD_TEXTURES_COUNT:
    {
      delete [] namePtr;
      delete [] vertexesData;
      delete [] texData;

      file.close();

      return OBJ_SKIN_ABS_BAD_TEXTURES_COUNT;
    }
    default:
    {
      break;
    }
  }

  char* jointsData = new char[volumes[2]];

  file.read((char*)jointsData, volumes[2]);

  switch(mainModel.amAbsorbSkeletonData(jointsData, counts[2]))
  {
    case ABS_BAD_SKELETON_DATA_POINTER:
    {
      delete [] namePtr;
      delete [] vertexesData;
      delete [] texData;
      delete [] jointsData;

      file.close();

      return OBJ_SKELETON_ABS_BAD_DATA_POINTER;
    }
    case ABS_BAD_SKELETON_JOINTS_COUNT:
    {
      delete [] namePtr;
      delete [] vertexesData;
      delete [] texData;
      delete [] jointsData;

      file.close();

      return OBJ_SKELETON_ABS_BAD_JOINTS_COUNT;
    }
    default:
    {
      break;
    }
  }

  char* animData = new char[volumes[3]];

  file.read((char*)animData, volumes[3]);

  switch(mainModel.amAbsorbAnimationData(animData))
  {
    case ABS_BAD_ANIM_DATA_POINTER:
    {
      delete [] namePtr;
      delete [] vertexesData;
      delete [] texData;
      delete [] jointsData;
      delete [] animData;

      file.close();

      return OBJ_ANIM_ABS_BAD_DATA_POINTER;
    }
    case ABS_NO_SUCH_JOINT_FOR_ANIM:
    {
      delete [] namePtr;
      delete [] vertexesData;
      delete [] texData;
      delete [] jointsData;
      delete [] animData;

      file.close();

      return OBJ_ANIM_ABS_NO_SUCH_JOINT_FOR_ANIM;
    }
    default:
    {
      break;
    }
  }

  if (isChar)
  {
    char* attacksData = new char[volumes[4]];

    file.read((char*)attacksData, volumes[4]);

    switch(acAbsorbAttacksData(attacksData))
    {
      case CHAR_ABS_BAD_DATA_POINTER:
      {
        break;
      }
      default:
      {
        break;
      }
    }

    delete [] attacksData;
  }

  delete [] namePtr;
  delete [] vertexesData;
  delete [] texData;
  delete [] jointsData;
  delete [] animData;
  
  file.close();

  return OBJ_DONE;
}

//=============================================================================

acrRet aCharacter::acAbsorbAttacksData(char* data)
{
  if (!data)
    return CHAR_ABS_BAD_DATA_POINTER;

  aString tmpString;
  size_t shift = 0;
  size_t intSize = sizeof(int);
  size_t floatSize = sizeof(float);
  size_t dwordSize = sizeof(DWORD);
  size_t sizeSize = sizeof(size_t);
  size_t charSize = tmpString.asGetCharSize();
  size_t tmpLength;
  int count;
  aItem<aAttack>* tmpItem;
  const long bufferLength = 4096;
  wchar_t charBuffer[bufferLength];

  memcpy(&count, data, intSize);
  shift += intSize;

  while(count--)
  {
    tmpItem = attacks.alAddItem();

    memcpy(&tmpItem->data.range, &data[shift], floatSize);
    shift += floatSize;

    memcpy(&tmpItem->data.timeout, &data[shift], dwordSize);
    shift += dwordSize;

    memcpy(&tmpLength, &data[shift], sizeSize);
    shift += sizeSize;

    memset(&charBuffer, 0, bufferLength*charSize);
    memcpy(charBuffer, &data[shift], tmpLength*charSize);
    shift += tmpLength*charSize;
    tmpItem->data.name = charBuffer;

    memcpy(&tmpLength, &data[shift], sizeSize);
    shift += sizeSize;

    memset(&charBuffer, 0, bufferLength*charSize);
    memcpy(charBuffer, &data[shift], tmpLength*charSize);
    shift += tmpLength*charSize;
    tmpItem->data.animName = charBuffer;
  }

  return CHAR_DONE;
}

//=============================================================================

aItem<aAttack>* aCharacter::acGetAttackByName(const aString& cap)
{
  aItem<aAttack>* tmpAttack = attacks.alGetFirstItem();

  while(tmpAttack)
  {
    if (tmpAttack->data.name == cap)
      return tmpAttack;

    tmpAttack = tmpAttack->next;
  }

  return 0;
}

//=============================================================================

aCharacter::~aCharacter()
{
}

//=============================================================================
